/// <reference types="dom-serial"/>
/**
 * serial state simple enum
 */
export enum SerialState {
    Uninitial,
    Unsupport,
    Disconnect,
    Connected,
}                                                    

class SerialCommon {
    _serial: Serial | null;
    _port: SerialPort | undefined;
    _ports: Array<SerialPort>;
    _reader: any;
    _writer: any;
    _state: SerialState;
    _readPromise: Promise<any> | null;
    _onmessage: Function | undefined;
    _onconnect: Function | undefined;
    _ondisconnect: Function | undefined;
    _baudRate: number;

    constructor(
        baudRate: number = 115200,
    ) {
        this._serial = null;
        this._port = undefined;
        this._ports = [];
        this._reader = null;
        this._writer = null;
        this._readPromise = null;

        this._onmessage = undefined;
        this._onconnect = undefined;
        this._ondisconnect = undefined;

        this._baudRate = baudRate;
        this._state = SerialState.Uninitial;

        if ("serial" in window.navigator == false) {
            this._state = SerialState.Unsupport;
            console.error("Brower is not Support Serial");
        } else {
            this._serial = navigator.serial;
            this._serial.onconnect = async () => {
                await this.connect();
                console.debug("onconnect")
                if (this._onconnect) this._onconnect();
            }
            this._serial.ondisconnect = async () => {
                this._state = SerialState.Disconnect;
                console.debug("ondisconnect")
                if (this._ondisconnect) this._ondisconnect();
            }
        }
    }

    get ports() { return this._ports };
    get state() { return this._state == SerialState.Connected };

    set baudRate(x: number) { this._baudRate = x };
    set onmessage(x: Function) { this._onmessage = x };
    set onconnect(x: Function) { this._onconnect = x };
    set ondisconnect(x: Function) { this._ondisconnect = x };

    async request(filters?: any) {
        this._port = await this._serial?.requestPort({ filters: filters });
        this._ports = await this._serial?.getPorts() || [];
    }
    /**
     * 选择已授权的第一个设备进行连接
     * @returns 是否连接成功
     */
    async connect(): Promise<boolean> {
        this._ports = await this._serial?.getPorts() || [];
        if (this._ports.length > 0) {
            this._port = this._ports[0];
        } else {
            return false;
        }
        return await this.open();
    }
    /**
     * 选择上一次连接的设备进行连接
     * @returns 是否连接成功
     */
    async open(): Promise<boolean> {
        return await this._openSerial()
    }
    /**
     * 连接核心实现
     * @returns 是否连接成功
     */
    async _openSerial(): Promise<boolean> {
        try {
            await this._port?.open({ baudRate: this._baudRate })
        } catch (error) {
            console.debug(error);
            return false;
        }
        this._state = SerialState.Connected;
        this._reader = this._port?.readable.getReader();
        this._writer = this._port?.writable.getWriter();
        this._readPromise = this._readTask();
        return true;
    }
    /**
     * 
     * @param data 需要写入的数据
     * @returns 是否写入成功
     */
    async write(data: Uint8Array) {
        if (this._port && this._state == SerialState.Connected) {
            await this._writer.write(data);
            console.debug(data);
            return true;
        }
        return false;
    }
    /**
     * 关闭串口
     * @returns 是否关闭成功
     */
    async close() {
        return await this._closeSerial()
    }
    /**
     * 关闭核心代码
     * @returns 是否关闭成功
     */
    async _closeSerial() {
        try {
            this._writer?.releaseLock();
            await this._reader?.cancel();
            await this._readPromise;
            await this._port?.close();
            this._state = SerialState.Disconnect;
            return true;
        } catch (error) {
            console.log(error)
            return false;
        }
    }
    /**
     * 读取任务， 读取信息后触发onmessage
     */
    async _readTask() {
        while (this._port && this._port.readable && this._state == SerialState.Connected) {
            try {
                for (; ;) {
                    const { value, done } = await this._reader.read();
                    if (done) break;
                    if (this._onmessage) {
                        this._onmessage(value);
                    }
                }
            } catch (error) {
                console.error(error)
            } finally {
                this._reader.releaseLock();
                this._state = SerialState.Disconnect;
            }
        }
        // console.debug('end read')
    }
}

export default {
    SerialCommon,
    SerialState,
}